/*
 * Galaxium Messenger
 * 
 * Copyright (C) 2005-2007 Philippe Durand <draekz@gmail.com>
 * 
 * License: GNU Lesser General Public License (LGPL)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

using System;
using System.IO;
using System.Text;
using System.Collections.Generic;
using System.Security.Cryptography;

using Galaxium.Core;
using Anculus.Core;

using csammisrun.OscarLib;
using csammisrun.OscarLib.Utility;

namespace Galaxium.Protocol.Aim
{
	public sealed class AimSession : AbstractSession
	{
		// OscarLib stuff
		private Session _aimSession;
		private string _server = "login.oscar.aol.com";
		private int _port = 5190;
		private bool _useftproxy = false;
		private System.Timers.Timer _statusRequestTimer = new System.Timers.Timer(50);
		private List<string> _statusRequests = new List<string> ();
		private List<string> _pendingRequests = new List<string> ();
		private List<string> _pendingIcons = new List<string> ();
		private Dictionary<string, IconInfo> _icons = new Dictionary<string, IconInfo> ();
		
		// Galaxium stuff
		private bool _disposed;
		private ContactCollection _contacts;
		private GroupCollection _groups;
		
		public override GroupCollection GroupCollection
		{
			get { return this._groups; }
		}
		
		public IEnumerable<IGroup> Groups
		{
			get { return this._groups; }
		}
		
		public override ContactCollection ContactCollection
		{
			get { return this._contacts; }
		}
		
		public AimSession (AimAccount account) : base (account)
		{
			_statusRequestTimer.Elapsed += StatusRequestTimerElapsed;
			
			_contacts = new ContactCollection ();
			_groups = new GroupCollection ();
			
			account.Presence = account.InitialPresence;
			
			_aimSession = new Session(account.UniqueIdentifier, account.Password);
			
			_aimSession.ClientCapabilities = Capabilities.AIMtoICQ | Capabilities.BuddyIcon | Capabilities.iChat |
				Capabilities.SendFiles | Capabilities.Chat | Capabilities.DirectIM;
			
			_aimSession.SetDefaultIdentification();
			_aimSession.ClientIdentification.ClientName = "Galaxium";
			_aimSession.Statuses.SetProfile(""); // What is this for?
			
			// Now we need to listen for a bunch of events.
			_aimSession.LoginStatusUpdate += LoginStatusUpdate;
			_aimSession.LoginCompleted += LoginCompleted;
			_aimSession.LoginFailed += LoginFailed;
			_aimSession.StatusUpdate += StatusUpdate;
			_aimSession.WarningMessage += WarningMessage;
			_aimSession.ErrorMessage += ErrorMessage;
			
			// SSI -- buddy and group "received" events are connected in ContactListFinished handler
			_aimSession.ContactListFinished += ContactListFinished;
			_aimSession.MasterGroupItemReceived += MasterGroupItemReceived;
			_aimSession.BuddyItemRemoved += BuddyItemRemoved;
			_aimSession.GroupItemRemoved += GroupItemRemoved;
			
			// User status/info
			_aimSession.Statuses.UserStatusReceived += UserStatusReceived;
			_aimSession.Statuses.UserOffline += UserOffline;
			_aimSession.Statuses.UserInfoReceived += UserInfoReceived;
			
			// ICBM
			_aimSession.Messages.MessageReceived += MessageReceived;
			_aimSession.Messages.TypingNotification += TypingNotification;
			
			// Buddy icons
			_aimSession.BuddyIconDownloadCompleted += BuddyIconDownloadCompleted;
			_aimSession.BuddyIconDownloadFailed += BuddyIconDownloadFailed;
			
			// File transfer
			_aimSession.FileTransferCancelled += FileTransferCancelled;
			_aimSession.FileTransferCompleted += FileTransferCompleted;
			_aimSession.FileTransferProgress += FileTransferProgress;
			_aimSession.FileTransferRequestReceived += FileTransferRequestReceived;
			
			// Direct Connection
			_aimSession.DirectIMRequestReceived += DirectIMRequestReceived;
			_aimSession.DirectIMSessionReady += DirectIMSessionReady;
			_aimSession.DirectIMSessionCancelled += DirectIMSessionCancelled;
			_aimSession.DirectIMSessionClosed += DirectIMSessionClosed;
			_aimSession.DirectIMReceived += DirectIMReceived;
			
			// Chat rooms
			_aimSession.ChatInvitationReceived += ChatInvitationReceived;
			_aimSession.ChatRooms.ChatRoomJoined += ChatRoomJoined;
		}
		
		protected override void Dispose (bool disposing)
		{
			//if (!_disposed)
				//if (disposing && _connection != null)
					//_connection.Dispose ();
			
			_disposed = true;
		}
		
		public override void Connect ()
		{
			_aimSession.Logon(_server, _port);
			
			OnConnected (new SessionEventArgs (this));
		}
		
		public override void Disconnect ()
		{
			OnDisconnected (new SessionEventArgs (this));
		}
		
		public void SetPresenceStatus (IPresence presence)
		{
			
		}
		
		private void StatusRequestTimerElapsed (object sender, EventArgs args)
		{
			lock (_statusRequests)
			{
				if (_statusRequests.Count > 0)
				{
					try
					{
						_aimSession.RequestUserInfo (_statusRequests[0], UserInfoRequest.AwayMessage | UserInfoRequest.UserProfile);
						_pendingRequests.Add (_statusRequests[0]);
						_statusRequests.RemoveAt (0);
					}
					catch
					{
						
					}
				}
				else
				{
					_statusRequestTimer.Stop ();
				}
			}
		}
		
		private void EnqueueStatusRequest (string screenname)
		{
			lock (_statusRequests)
			{
				if (!_statusRequests.Contains (screenname) && !_pendingRequests.Contains (screenname))
				{
					_statusRequests.Add (screenname);
					
					if (!_statusRequestTimer.Enabled)
						_statusRequestTimer.Start ();
				}
			}
		}
		
		private void DequeueStatusRequest (string screenname)
		{
			lock (_statusRequests)
			{
				if (_statusRequests.Contains (screenname))
					_statusRequests.Remove (screenname);
				
				if (_pendingRequests.Contains (screenname))
					_pendingRequests.Remove (screenname);
				
				if (_statusRequests.Count < 1)
					if (_statusRequestTimer.Enabled)
						_statusRequestTimer.Stop ();
			}
		}
		
		private void CheckDisplayImage (IContact contact, IconInfo icon)
		{
			if (icon != null)
			{
				lock (_icons)
				{
					if (_icons.ContainsKey (contact.UniqueIdentifier))
					{
						if (_icons[contact.UniqueIdentifier].ToString () != icon.ToString ())
							_icons[contact.UniqueIdentifier] = icon;
					}
					else
						_icons.Add (contact.UniqueIdentifier, icon);
				}
				
				DownloadDisplayImage (contact);
			}
			else
			{
				lock (_icons)
				{
					if (_icons.ContainsKey (contact.UniqueIdentifier))
					{
						_icons.Remove (contact.UniqueIdentifier);
						return;
					}
				}
			}
		}
		
		public void UploadDisplayImage (string filename)
		{
			_aimSession.SetBuddyIcon(filename);
		}
		
		public void DownloadDisplayImage (IContact contact)
		{
			if (_aimSession != null && _aimSession.LoggedIn)
			{
				lock (_pendingIcons)
					if (_pendingIcons.Contains (contact.UniqueIdentifier))
						return;
				
				IconInfo icon = null;
				
				lock (_icons)
				{
					foreach (string key in _icons.Keys)
						if (key.CompareTo (contact.UniqueIdentifier) == 0)
							icon = _icons[contact.UniqueIdentifier];
				}
				
				if (icon == null)
				{
					EnqueueStatusRequest (contact.UniqueIdentifier);
					return;
				}
				
				lock (_pendingIcons)
				{
					_pendingIcons.Add (contact.UniqueIdentifier);
					_aimSession.GetBuddyIcon (contact.UniqueIdentifier, icon);
				}
			}
		}
		
		void UserInfoReceived(object sender, UserInfoResponse info)
		{
			Log.Debug ("Received user info: "+info.Info.ScreenName);
			
			AimContact contact = ContactCollection.GetContact (info.Info.ScreenName) as AimContact;
			
			// Here we just check for updates to the user status. We can get the away message here.
			
			if (contact != null)
			{
				CheckDisplayImage (contact, info.Info.Icon);
				
				IPresence oldPresence = contact.Presence;
				
				if (!String.IsNullOrEmpty (info.AwayMessage))
				{
					contact.Presence = AimPresence.Away;
					
					string message = info.AwayMessage.Substring(info.AwayMessage.IndexOf("<html><body>")+12, info.AwayMessage.IndexOf("</body></html>")-12);
					
					contact.DisplayMessage = message;
				}
				else
					contact.Presence = AimPresence.Online;
				
				EmitContactPresenceChanged (new EntityChangeEventArgs<IPresence> (contact, contact.Presence, oldPresence));
			}
			else
			{
				Log.Error ("Could not find contact in collection: "+info.Info.ScreenName);
				return;
			}
			
			DequeueStatusRequest (info.Info.ScreenName);
        	
        	
            /*// Find the current user status
            ShaimContactStatus status = ShaimContactStatus.Online;
            if (!String.IsNullOrEmpty(info.AwayMessage))
            {
                status |= ShaimContactStatus.Away;
            }
            else
            {
                status &= ~ShaimContactStatus.Away;
            }

            if (info.Info.IdleTime > 0)
            {
                status |= ShaimContactStatus.Idle;
            }
            else
            {
                status &= ~ShaimContactStatus.Idle;
            }

            string key = UsernameFormatter(info.Info.ScreenName);
            DequeueStatusRequest(key);

            // Do icon request
            CheckForBuddyIcon(key, info.Info.Icon);

            // TODO:  I don't know if this is what we want, or we want SignonTime (epoch)
            string statusmessage = ParsePercentVariables(info.AwayMessage);
            string profile = ParsePercentVariables(info.Profile);

            List<ShaimContact> contacts = GetContacts(key);
            ShaimContactUpdateEvent evnt = new ShaimContactUpdateEvent();

            for (int i = 0; i < contacts.Count; i++)
            {
                ShaimContact sc = contacts[i];
                ContactChangeTypes cct = ContactChangeTypes.None;

                // Update name
                sc.Name = info.Info.ScreenName;

                // Update info
                if (sc.Info.IdleTime != info.Info.IdleTime)
                {
                    sc.Info.IdleTime = info.Info.IdleTime;
                    cct |= ContactChangeTypes.ContactInfoNonString;
                }
                if (sc.Info.Online != info.Info.SignonTime.ToLocalTime())
                {
                    sc.Info.Online = info.Info.SignonTime.ToLocalTime();
                    cct |= ContactChangeTypes.ContactInfoNonString;
                }

                if (sc.Info.StatusMessage != statusmessage)
                {
                    sc.Info.StatusMessage = statusmessage;
                    cct |= ContactChangeTypes.ContactInfoString;
                }

                if (sc.Info.Profile != profile)
                {
                    sc.Info.Profile = profile;
                    cct |= ContactChangeTypes.ContactInfoString;
                }

                // Update status
                if (sc.Status != status)
                {
                    sc.Status = status;
                    cct |= ContactChangeTypes.ContactStatus;
                }

                evnt.Changes.Add(new ContactChange(sc, cct));
            }
            shaimCore.RaiseShaimEvent(evnt);*/
		}
		
		void UserStatusReceived(object sender, UserInfo userinfo)
		{
			Log.Debug ("User status received: "+userinfo.ScreenName);
			
			// When we get a user status message, they are automatically online?
			// The data here doesn't give us a actual "status" per say.
			
			AimContact contact = ContactCollection.GetContact (userinfo.ScreenName) as AimContact;
			
			if (contact != null)
			{
				CheckDisplayImage (contact, userinfo.Icon);
				
				IPresence oldPresence = contact.Presence;
				
				if (contact.Presence == AimPresence.Offline)
				{
					contact.Presence = AimPresence.Online;
					
					string message = userinfo.AvailableMessage.Substring(userinfo.AvailableMessage.IndexOf("<html><body>")+12, userinfo.AvailableMessage.IndexOf("</body></html>")-12);
					
					contact.DisplayMessage = message;
					
					EmitContactPresenceChanged (new EntityChangeEventArgs<IPresence> (contact, contact.Presence, oldPresence));
				}
				
				if (userinfo.IdleTime > 0)
				{
					// The user has idle time, we could emulate idle status?
				}
				
				if (userinfo.Class == UserClass.Away)
					Log.Debug ("User is away");
			}
			else
			{
				Log.Error ("Unable to find contact in collection: "+userinfo.ScreenName);
				return;
			}
			
			EnqueueStatusRequest (userinfo.ScreenName);
        	
            /*string key = UsernameFormatter(userinfo.ScreenName);
            CheckForBuddyIcon(key, userinfo.Icon);

            List<ShaimContact> contacts = GetContacts(key);
            ShaimContactUpdateEvent evnt = new ShaimContactUpdateEvent();
            for (int i = 0; i < contacts.Count; i++)
            {
                ShaimContact sc = contacts[i];

                ContactChangeTypes cct = ContactChangeTypes.None;

                // Update name
                sc.Name = userinfo.ScreenName;

                // Update info
                if (sc.Info.IdleTime != userinfo.IdleTime)
                {
                    sc.Info.IdleTime = userinfo.IdleTime;
                    cct |= ContactChangeTypes.ContactInfoNonString;
                }

                if (sc.Info.Online != userinfo.SignonTime.ToLocalTime())
                {
                    sc.Info.Online = userinfo.SignonTime.ToLocalTime();
                    cct |= ContactChangeTypes.ContactInfoNonString;
                }

                // Calculate the status
                ShaimContactStatus oldstatus = sc.Status;
                // If the contact is offline, take it online
                if (sc.Status == ShaimContactStatus.Offline)
                {
                    sc.Status = ShaimContactStatus.Online;
                }
                // Reset the idle bit
                if (userinfo.IdleTime > 0)
                {
                    sc.Status |= ShaimContactStatus.Idle;
                }
                else
                {
                    sc.Status &= ~ShaimContactStatus.Idle;
                }

                if (sc.Status != oldstatus)
                {
                    cct |= ContactChangeTypes.ContactStatus;
                }

                // Check the wireless user bit
                sc.Info.IsMobile = (userinfo.Class & UserClass.Wireless) != 0;

                evnt.Changes.Add(new ContactChange(sc, cct));
            }

            shaimCore.RaiseShaimEvent(evnt);

            EnqueueStatusRequest(key);*/
		}
		
		void UserOffline(object sender, UserInfo userinfo)
		{
			Log.Debug ("User offline received: "+userinfo.ScreenName);
			
			AimContact contact = ContactCollection.GetContact (userinfo.ScreenName) as AimContact;
			
			if (contact != null)
			{
				IPresence oldPresence = contact.Presence;
				
				if (contact.Presence != AimPresence.Offline)
				{
					contact.Presence = AimPresence.Offline;
					EmitContactPresenceChanged (new EntityChangeEventArgs<IPresence> (contact, contact.Presence, oldPresence));
				}
			}
			else
			{
				Log.Error ("Unable to find contact in collection: "+userinfo.ScreenName);
				return;
			}
			
			DequeueStatusRequest (userinfo.ScreenName);
			
            /*string key = UsernameFormatter(userinfo.ScreenName);
            DequeueStatusRequest(key);
            
            List<ShaimContact> contacts = GetContacts(key);
            ShaimContactUpdateEvent evnt = new ShaimContactUpdateEvent();
            for (int i = 0; i < contacts.Count; i++)
            {
                ShaimContact sc = contacts[i];
                if (sc.Status != ShaimContactStatus.Offline)
                {
                    sc.Status = ShaimContactStatus.Offline;
                    sc.Name = userinfo.ScreenName;

                    evnt.Changes.Add(new ContactChange(sc, ContactChangeTypes.ContactStatus));
                }
            }

            if (evnt.Changes.Count > 0)
            {
                shaimCore.RaiseShaimEvent(evnt);
            }*/
		}
		
		void MessageReceived(object sender, MessageReceivedEventArgs e)
		{
			Log.Debug ("Message received from: "+e.Message.ScreenName);
		}
		
		void TypingNotification(object sender, TypingNotificationEventArgs e)
		{
			Log.Debug ("Typing notification received from: "+e.ScreenName);
            /*string key = UsernameFormatter(e.ScreenName);
            ShaimTypingNotificationReceivedEvent evnt = new ShaimTypingNotificationReceivedEvent();
            evnt.Contact = GetContacts(key)[0];

            if (e.Notification == TypingNotification.TypingStarted)
            {
                evnt.Notification = ShaimTypingNotification.TypingStarted;
            }
            else
            {
                evnt.Notification = ShaimTypingNotification.TypingStopped;
            }

            shaimCore.RaiseShaimEvent(evnt);*/
		}
		
		void ChatRoomJoined(object sender, ChatRoom newroom)
		{
			Log.Debug ("Chat room joined.");
            /*// Attach events to the chat room
            newroom.UserJoined += ChatRoom_UserJoined;
            newroom.UserLeft += ChatRoom_UserLeft;
            newroom.MessageReceived += ChatRoom_MessageReceived;
            // Signal the client that a that room was created
            ShaimChatRoomCreatedEvent evnt = new ShaimChatRoomCreatedEvent();
            evnt.ChatRoom = GetChatRoom(newroom);
            shaimCore.RaiseShaimEvent(evnt);*/
		}
		
		void ChatInvitationReceived(Session sess, UserInfo sender, string roomname, string message, Encoding encoding, string language, Cookie key)
		{
			Log.Debug ("Chat invitation recieved.");
            /*ShaimChatRoomInvitationEvent evnt = new ShaimChatRoomInvitationEvent();
            evnt.Inviter = GetContacts(sender.ScreenName)[0];
            evnt.Message = message;
            evnt.ChatRoom = new ShaimChatRoom(roomname, this, key);

            shaimCore.RaiseShaimEvent(evnt);*/
		}
		
		void ErrorMessage(Session sess, ServerErrorCode errorcode)
		{
			Log.Debug ("Error message received.");
			
            /*shaimCore.LogMessage(this, ShaimLogLevel.Error, "Session sent error message {0}", errorcode);
            sess.Logoff();

            ShaimProtocolError spe = ShaimProtocolError.Unknown;

            // Figure out what the message means
            switch (errorcode)
            {
                case ServerErrorCode.LostBOSConnection:
                    spe = ShaimProtocolError.CantReachNetwork;
                    break;
            }

            ShaimProtocolErrorEvent evnt = new ShaimProtocolErrorEvent();
            evnt.Plugin = this;
            evnt.Error = spe;
            shaimCore.RaiseShaimEvent(evnt);

            Disconnect(new ShaimSignOutEvent());*/
		}
		
		void WarningMessage(Session sess, ServerErrorCode sec)
		{
			Log.Debug ("Warning message received.");
			
            /*ShaimProtocolWarningEvent evnt = new ShaimProtocolWarningEvent();
            evnt.Message = this.PluginInfo.Name + " (" + this.Username + "): " +
              Enum.GetName(typeof(ServerErrorCode), sec);
            evnt.Plugin = this;
            shaimCore.RaiseShaimEvent(evnt);*/
		}
		
		void StatusUpdate(Session sess, string message)
		{
			Log.Debug ("Status update received: "+message);
			
            /*ShaimProtocolStatusEvent evnt = new ShaimProtocolStatusEvent();
            evnt.Message = message;
            evnt.Plugin = this;
            shaimCore.RaiseShaimEvent(evnt);*/
		}
		
		void LoginFailed(Session sess, LoginErrorCode errorcode)
		{
			Log.Debug ("Login failed received.");
			
			this.EmitErrorOccurred( new ErrorEventArgs (this, errorcode.ToString(), errorcode.ToString()));
			
            /*ShaimProtocolError spe = ShaimProtocolError.Unknown;
            if (errorcode == LoginErrorCode.IncorrectScreennamePassword ||
              errorcode == LoginErrorCode.InvalidScreennamePassword)
            {
                spe = ShaimProtocolError.WrongUsernamePassword;
            }
            else if (errorcode == LoginErrorCode.RateLimitExceeded)
            {
                spe = ShaimProtocolError.LoggedInTooOften;
            }
            else if (errorcode == LoginErrorCode.CantReachAuthServer ||
              errorcode == LoginErrorCode.CantReachBOSServer)
            {
                spe = ShaimProtocolError.CantReachNetwork;
            }

            ShaimConnectionFailedEvent evnt = new ShaimConnectionFailedEvent();
            evnt.Error = spe;
            evnt.Plugin = this;
            shaimCore.RaiseShaimEvent(evnt);*/
		}
		
		void LoginStatusUpdate(Session sess, string message, double percentdone)
		{
			EmitLoginProgress (new SessionProgressEventArgs (this, message, percentdone));
		}
		
		new void LoginCompleted(Session sess)
		{
			Log.Debug ("Login completed received.");
		}
		
		void SSIEditComplete(Session sess)
		{
			Log.Debug ("SSI edit complete received.");
			
            /*lock (_ssieditqueue)
            {
                if (_ssieditqueue.Count == 0)
                {
                    return;
                }

                SSIEdit edit = _ssieditqueue.Dequeue();
                switch (edit.Type)
                {
                    case SSIEditType.Add:
                        if (edit.Object is SSIBuddy)
                        {
                            SSIBuddy buddy = edit.Object as SSIBuddy;
                            _session_BuddyItemReceived(sess, buddy);
                        }
                        else if (edit.Object is SSIGroup)
                        {
                            SSIGroup group = edit.Object as SSIGroup;
                            shaimCore.ContactList.InsertGroup(group.Name, 0);
                        }
                        break;
                    case SSIEditType.Remove:
                        if (edit.Object is SSIGroup)
                        {
                            _session_GroupItemRemoved(sess, edit.Object as SSIGroup);
                        }
                        break;
                }

                if (edit.Object is ShaimContact)
                {
                    if (edit.Type == SSIEditType.Edit)
                    {
                        // OH GOD THIS IS SO AWKWARD
                        ShaimContact sc = edit.Object as ShaimContact;
                        sc.Parent.RequestDisplayNameUpdate(this, sc.DisplayName);

                        // Make sure anything displaying the contact gets the message
                        ShaimContactUpdateEvent evnt = new ShaimContactUpdateEvent();
                        evnt.Changes.Add(new ContactChange(sc, ContactChangeTypes.None));
                        shaimCore.RaiseShaimEvent(evnt);
                    }
                    else if (edit.Type == SSIEditType.Remove)
                    {
                        ShaimContact sc = edit.Object as ShaimContact;
                        shaimCore.ContactList.Unmerge(sc);
                    }
                }
            }*/
		}
		
		void ContactListFinished(Session sess, DateTime lastModifiedDate)
		{
			Log.Debug ("Parsing contact list data.");
			
			lock (_aimSession.SSI)
			{
				foreach (SSIGroup aimGroup in _aimSession.SSI.GetSSIItems<SSIGroup> ())
				{
					if (aimGroup.ID > 0)
					GroupCollection.Add (new AimGroup (this, aimGroup.ID.ToString(), aimGroup.Name, false));
				}
				
				foreach (SSIBuddy buddy in _aimSession.SSI.GetSSIItems<SSIBuddy> ())
				{
					AimContact contact = new AimContact (this, buddy.Name);
					contact.DisplayName = buddy.DisplayName;
					contact.Load ();
					
					AimGroup group = GroupCollection.GetGroup (buddy.GroupID.ToString()) as AimGroup;
					group.Add (contact);
					
					ContactCollection.Add (contact);
				}
			}
			
			// At this point we have the contact lists built.
			
			_aimSession.ActivateBuddyList ();
			
			EmitLoginCompleted (new SessionEventArgs (this));
		}
		
		void MasterGroupItemReceived(Session sess, int numgroups)
		{
			Log.Debug ("Master group item received.");
		}
		
		void BuddyItemRemoved(Session sess, SSIBuddy buddy)
		{
			Log.Debug ("Buddy item removed received.");
			
            /*List<ShaimContact> contacts = GetContacts(UsernameFormatter(buddy.Name));
            ShaimContact target = null;
            foreach (ShaimContact contact in contacts)
            {
                if ((contact.RawContact is SSIBuddy) && (contact.RawContact as SSIBuddy).ItemID == buddy.ItemID)
                {
                    target = contact;
                    break;
                }
            }

            if (target != null)
            {
                shaimCore.ContactList.Unmerge(target);
            }*/
		}
		
		void GroupItemRemoved(Session sess, SSIGroup group)
		{
			Log.Debug ("Group item removed received.");
			
            /*ShaimContactGroup target = null;
            for (int i = 0, j = shaimCore.ContactList.Groups.Count; i < j; i++)
            {
                if (shaimCore.ContactList.Groups[i].DisplayName == group.Name)
                {
                    target = shaimCore.ContactList.Groups[i];
                    break;
                }
            }

            if (target != null)
            {
                shaimCore.ContactList.Unmerge(target);
            }*/
		}
		
		void GroupItemReceived(Session sess, SSIGroup group)
		{
			Log.Debug ("Group item received.");
			
            /*if (outstandingListOperations.ContainsKey(group))
            {
                foreach (SSIBuddy buddy in outstandingListOperations[group])
                {
                    RegisterContactFromBuddy(buddy, group);
                }
                outstandingListOperations[group] = null;
                outstandingListOperations.Remove(group);
            }*/
		}
		
		void BuddyItemReceived(Session sess, SSIBuddy buddy)
		{
			Log.Debug ("Buddy item received.");
			
            /*if (buddy == null)
            {
                return;
            }

            SSIGroup parent = null;
            foreach (SSIGroup grp in sess.SSI.GetSSIItems<SSIGroup>())
            {
                if (grp != null && grp.ID == buddy.GroupID)
                {
                    parent = grp;
                    break;
                }
            }

            if (parent != null && parent.Name != "Mobile Device")
            {
                // Check to see if the parent group has had its children updated yet
                if (parent.Children.IndexOf(buddy.ItemID) == -1)
                {
                    // Store the contact to be added when the group modification comes in
                    if (!outstandingListOperations.ContainsKey(parent))
                    {
                        outstandingListOperations[parent] = new List<SSIBuddy>();
                    }
                    outstandingListOperations[parent].Add(buddy);
                }
                else
                {
                    RegisterContactFromBuddy(buddy, parent);
                }
            }*/
		}
		
		void BuddyIconDownloadCompleted(Session sess, string screenname, Stream iconstream)
		{
			Log.Debug ("Buddy icon download completed for: "+screenname);
			
			lock (_pendingIcons)
			{
				if (_pendingIcons.Contains (screenname))
					_pendingIcons.Remove (screenname);
			}
			
			AimContact contact = ContactCollection.GetContact (screenname) as AimContact;
			
			if (contact != null)
			{
				AimDisplayImage oldImage = contact.DisplayImage as AimDisplayImage;
				contact.DisplayImage = new AimDisplayImage(this);
				
				byte[] buffer = new byte[iconstream.Length];
				iconstream.Read (buffer, 0, (int)iconstream.Length);
				(contact.DisplayImage as AimDisplayImage).ImageBuffer = buffer;
				
				Log.Debug ("Firing display image event.");
				
				EmitContactDisplayImageChanged (new EntityChangeEventArgs<IDisplayImage> (contact, contact.DisplayImage, oldImage));
			}
			
            /*string key = UsernameFormatter(screenname);
            lock (_pendingicons)
            {
                try
                {
                    string filename = GetBuddyIconPath(iconstream, _iconinfo[key]);

                    using (FileStream fs = new FileStream(filename, FileMode.Create))
                    {
                        int datalen = (int)iconstream.Length;
                        byte[] data = new byte[datalen];
                        iconstream.Read(data, 0, datalen);
                        fs.Write(data, 0, datalen);
                    }

                    List<ShaimContact> contacts = GetContacts(key);
                    foreach (ShaimContact sc in contacts)
                    {
                        sc.Info.IconPath = filename;
                    }
                }
                finally
                {
                    _pendingicons.Remove(key);
                }
            }*/
		}
		
		void BuddyIconDownloadFailed(Session sess, string screenname, string reason)
		{
			Log.Debug ("Buddy icon download failed for ["+screenname+"]: "+reason);
			
			lock (_pendingIcons)
			{
				if (_pendingIcons.Contains (screenname))
					_pendingIcons.Remove (screenname);
			}
		}
		
		void FileTransferRequestReceived(Session sess, UserInfo sender, string IP, string filename, uint filesize, string message, Cookie key)
		{
			Log.Debug ("File transfer request received.");
			
            /*ShaimContact sc = GetContacts(sender.ScreenName)[0];

            ShaimFileTransfer sft = new ShaimFileTransfer(this, sc, true);
            sft.InternalKey = key;
            sft.UpdateProgress(0, (long)filesize);
            sft.Message = String.IsNullOrEmpty(message) ? "Remote IP: " + IP : message;

            ShaimFileTransferInvitationEvent evnt = new ShaimFileTransferInvitationEvent();
            evnt.FileName = filename;
            evnt.FileTransfer = sft;
            evnt.FileSize = (long)filesize;
            evnt.Message = sft.Message;
            shaimCore.RaiseShaimEvent(evnt);*/
		}
		
		void FileTransferProgress(Session sess, Cookie cookie, uint BytesTransfered, uint BytesTotal)
		{
			Log.Debug ("File transfer progress.");
			
            /*ShaimFileTransfer sft = GetFileTransfer(cookie, false);
            if (sft != null)
            {
                sft.UpdateProgress(BytesTransfered, BytesTotal);

                ShaimFileTransferEvent evnt = new ShaimFileTransferEvent();
                evnt.EventType = FileTransferEventType.Progress;
                evnt.FileTransfer = sft;
                shaimCore.RaiseShaimEvent(evnt);
            }*/
		}
		
		void FileTransferCompleted(Session sess, Cookie cookie)
		{
			Log.Debug ("File transfer completed.");
			
            /*ShaimFileTransfer sft = GetFileTransfer(cookie, true);
            if (sft != null)
            {
                sft.Complete = true;

                ShaimFileTransferEvent evnt = new ShaimFileTransferEvent();
                evnt.EventType = FileTransferEventType.Completion;
                evnt.FileTransfer = sft;
                shaimCore.RaiseShaimEvent(evnt);
            }*/
		}
		
		void FileTransferCancelled(Session sess, UserInfo other, Cookie cookie, string reason)
		{
			Log.Debug ("File transfer cancelled.");
			
            /*ShaimFileTransfer sft = GetFileTransfer(cookie, true);
            if (sft != null)
            {
                sft.Canceled = true;

                ShaimFileTransferEvent evnt = new ShaimFileTransferEvent();
                evnt.EventType = FileTransferEventType.Cancellation;
                evnt.FileTransfer = sft;
                evnt.Message = reason;
                shaimCore.RaiseShaimEvent(evnt);
            }*/
		}
		
		public override IFileTransfer SendFile (IContact contact, string fileName)
		{
			ThrowUtility.ThrowIfNull ("contact", contact);
			ThrowUtility.ThrowIfEmpty ("fileName", fileName);
			
			return null;
		}
		
		void DirectIMSessionReady(Session sess, UserInfo other, Cookie cookie)
		{
			Log.Debug ("Direct IM session ready.");
			
            /*List<ShaimContact> contacts = GetContacts(other.ScreenName);
            if (contacts.Count > 0)
            {
                // Alert the user
                ShaimMessageReceivedEvent smre = new ShaimMessageReceivedEvent();
                smre.Contact = contacts[0];
                smre.Flags = MessageParameters.SystemMessage;
                smre.Message = "Directly connected to " + contacts[0].Parent.DisplayName;
                shaimCore.RaiseShaimEvent(smre);

                // Reset the UI menu items and such
                _directIMsessions.Add(cookie, contacts[0]);
                RegisterDirectIMMenu();
            }*/
		}
		
		void DirectIMSessionCancelled(Session sess, UserInfo other, Cookie cookie, string reason)
		{
			Log.Debug ("Direct IM session cancelled.");
			
            /*List<ShaimContact> contacts = GetContacts(other.ScreenName);
            if (contacts.Count > 0)
            {
                ShaimMessageReceivedEvent smre = new ShaimMessageReceivedEvent();
                smre.Contact = contacts[0];
                smre.Flags = MessageParameters.SystemMessage;
                smre.Message = "The direct connection was cancelled: " + reason;
                shaimCore.RaiseShaimEvent(smre);

                _directIMsessions.Remove(cookie);
                DeregisterDirectIMMenu();
            }*/
		}
		
		void DirectIMSessionClosed(Session sess, UserInfo other, Cookie cookie)
		{
			Log.Debug ("Direct IM session closed.");
			
            /*List<ShaimContact> contacts = GetContacts(other.ScreenName);
            if (contacts.Count > 0)
            {
                // Alert the user
                ShaimMessageReceivedEvent smre = new ShaimMessageReceivedEvent();
                smre.Contact = contacts[0];
                smre.Flags = MessageParameters.SystemMessage;
                smre.Message = "The direct connection has been closed";
                shaimCore.RaiseShaimEvent(smre);

                _directIMsessions.Remove(cookie);
                DeregisterDirectIMMenu();
            }*/
		}
		
		void DirectIMRequestReceived(Session sess, UserInfo sender, string message, Cookie key)
		{
			Log.Debug ("Direct IM request received.");
			
            /*ShaimDirectConnectionInvitationEvent sdcie = new ShaimDirectConnectionInvitationEvent();
            sdcie.Contact = GetContacts(sender.ScreenName)[0];
            sdcie.InternalKey = key;
            sdcie.Message = String.IsNullOrEmpty(message) ? "" : message;
            shaimCore.RaiseShaimEvent(sdcie);*/
		}
		
		void DirectIMReceived(Session sess, DirectIM message)
		{
			Log.Debug ("Direct IM received.");
			
			//ProcessIncomingMessage(message);
		}
		
		public IEnumerable<AimGroup> GetGroupsForContact (AimContact contact)
		{
			foreach (AimGroup group in _groups) {
				if (group.Contains (contact))
					yield return group;
			}
		}
				
		public override void SetPresence (BasePresence presence)
		{
			//TODO: This is called when we set the status with the tray icon
			// it needs to find the correct IPresence object & call SetPresenceStatus
		}
	}
}